package com.example.prom.aspect;

import com.alibaba.fastjson.JSON;
import com.example.prom.annotation.ServiceLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * Description
 *
 * @author DangWen
 */
@Component
@Aspect
@Slf4j
public class ServiceLogAspect {

    public ServiceLogAspect() {
        log.info("ServiceLogAspect init.......");
    }

    // 修改切面表达式，支持类级别和方法级别的@ServiceLog注解
    @Around("@annotation(serviceLog) || @within(serviceLog)")
    public Object logServiceMethodExecutionTime(ProceedingJoinPoint joinPoint, ServiceLog serviceLog) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        String methodName = methodSignature.getName();

        if (shouldIgnoreMethod(methodName, joinPoint, serviceLog)) {
            // 如果被标记为ignore的方法，直接执行并返回结果，不记录日志
            return joinPoint.proceed();
        }

        // 获取tag信息
        String[] tags = getTags(joinPoint, serviceLog);

        long startTime = System.currentTimeMillis();
        Object result;
        try {
            result = joinPoint.proceed(); // 执行方法
        } catch (Throwable throwable) {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            String className = methodSignature.getDeclaringType().getSimpleName();
            Object[] args = joinPoint.getArgs();

            log.error("[{}] {} {} 切面出现异常 after {} ms with params: {}",
                    formatTags(tags), className, methodName, duration,
                    formatMethodParams(methodSignature, args));
            throw throwable;
        }

        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;

        String className = methodSignature.getDeclaringType().getSimpleName();
        Object[] args = joinPoint.getArgs();

        log.info("[{}] 方法入参: {}", formatTags(tags), formatMethodParams(methodSignature, args));
        log.info("[{}] 方法出参: {}", formatTags(tags), safeToJson(result));
        log.info("[{}] {} {} 方法总耗时 {} ms", formatTags(tags), className, methodName, duration);

        return result;
    }

    private String[] getTags(ProceedingJoinPoint joinPoint, ServiceLog serviceLog) {
        // 如果方法上有注解且有tags，优先使用方法上的tags
        if (serviceLog != null && serviceLog.tags().length > 0) {
            return serviceLog.tags();
        }

        // 如果方法上没有注解或没有tags，则检查类上的注解
        Object target = joinPoint.getTarget();
        ServiceLog classServiceLog = target.getClass().getAnnotation(ServiceLog.class);
        if (classServiceLog != null && classServiceLog.tags().length > 0) {
            return classServiceLog.tags();
        }

        // 如果都没有tags，返回空数组
        return new String[0];
    }

    private String formatTags(String[] tags) {
        if (tags == null || tags.length == 0) {
            return "DEFAULT";
        }
        return String.join(",", tags);
    }

    private String formatMethodParams(MethodSignature methodSignature, Object[] args) {
        try {
            String[] paramNames = methodSignature.getParameterNames();
            if (paramNames == null || args == null || paramNames.length != args.length) {
                return safeToJson(args);
            }

            StringBuilder sb = new StringBuilder();
            sb.append("{");
            for (int i = 0; i < paramNames.length; i++) {
                if (i > 0) {
                    sb.append(", ");
                }
                sb.append("\"").append(paramNames[i]).append("\": ");
                sb.append(safeToJson(args[i]));
            }
            sb.append("}");
            return sb.toString();
        } catch (Exception e) {
            // 如果获取参数名失败，回退到原来的数组格式
            return safeToJson(args);
        }
    }

    // 更新方法签名，增加joinPoint参数以便获取目标类上的注解
    private boolean shouldIgnoreMethod(String methodName, ProceedingJoinPoint joinPoint, ServiceLog serviceLog) {
        // 获取目标对象
        Object target = joinPoint.getTarget();
        // 获取目标类上的ServiceLog注解
        ServiceLog classServiceLog = target.getClass().getAnnotation(ServiceLog.class);

        Set<String> ignoreMethodSet = new HashSet<>();
        // 如果方法上有注解，优先使用方法上的ignoreMethods
        if (serviceLog != null) {
            ignoreMethodSet.addAll(Arrays.asList(serviceLog.ignoreMethods()));
        }
        // 如果类上有注解且方法上没有注解，则使用类上的ignoreMethods
        else if (classServiceLog != null) {
            ignoreMethodSet.addAll(Arrays.asList(classServiceLog.ignoreMethods()));
        }
        return ignoreMethodSet.contains(methodName);
    }

    private String safeToJson(Object obj) {
        try {
            // 如果是数组类型，则逐个检查元素
            if (obj instanceof Object[]) {
                Object[] array = (Object[]) obj;
                Object[] filteredArray = new Object[array.length];
                for (int i = 0; i < array.length; i++) {
                    if (array[i] instanceof java.util.List) {
                        filteredArray[i] = "[List数组不打印]";
                    } else {
                        filteredArray[i] = array[i];
                    }
                }
                return JSON.toJSONString(filteredArray);
            }
            // 如果参数本身就是 List 类型，直接返回占位符
            if (obj instanceof java.util.List) {
                return "[List数组不打印]";
            }
            // 其他类型正常序列化
            return JSON.toJSONString(obj);
        } catch (Exception e) {
            return "[Serialization Failed]";
        }
    }
}

